package middleware

import (
	"manage-server/global"
	"manage-server/util"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
)

var skipPaths = []string{"/api/v1/manage/admin/login"}

type JWTClaims struct {
	ID       uint   `json:"id"`
	UserName string `json:"username"`
	jwt.StandardClaims
}

// 生成 Token
func CreateToken(id uint, username string) (string, error) {
	// 过期时间
	expireTime := time.Now().Add(2 * time.Hour)
	// 当前时间
	nowTime := time.Now()
	claims := JWTClaims{
		ID:       id,
		UserName: username,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),             //过期时间戳
			IssuedAt:  nowTime.Unix(),                //当前时间戳
			Issuer:    global.App.Config.Jwt.Issuer,  // 颁发者
			Subject:   global.App.Config.Jwt.Subject, // 签名主题
		},
	}
	tokenStruct := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return tokenStruct.SignedString([]byte(global.App.Config.Jwt.Secret))
}

// 验证 Token
func CheckToken(token string) (*JWTClaims, bool) {
	tokenObj, _ := jwt.ParseWithClaims(token, &JWTClaims{}, func(t *jwt.Token) (interface{}, error) {
		return []byte(global.App.Config.Jwt.Secret), nil
	})
	if key, _ := tokenObj.Claims.(*JWTClaims); tokenObj.Valid {
		return key, true
	} else {
		return nil, false
	}
}

func JWTAuthMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// 跳过路由鉴权
		for _, path := range skipPaths {
			if path == ctx.FullPath() {
				ctx.Next()
				return
			}
		}
		// 获取 token
		tokenString := ctx.Request.Header.Get("Authorization")
		// token不存在
		if tokenString == "" {
			util.Fail(ctx, util.R{
				P: util.P{
					Code:    http.StatusUnauthorized,
					Message: "没有权限",
				},
			})
			ctx.Abort()
			return
		}
		// token 格式错误
		tokenSlice := strings.SplitN(tokenString, " ", 2)
		if len(tokenSlice) != 2 && tokenSlice[0] != "Bearer" {
			util.Fail(ctx, util.R{
				P: util.P{
					Code:    http.StatusUnauthorized,
					Message: "没有权限",
				},
			})
			ctx.Abort()
			return
		}
		// 验证 token
		tokenStruck, ok := CheckToken(tokenSlice[1])
		if !ok {
			util.Fail(ctx, util.R{
				P: util.P{
					Code:    http.StatusUnauthorized,
					Message: "没有权限",
				},
			})
			ctx.Abort()
			return
		}

		// token 超时
		if time.Now().Unix() > tokenStruck.ExpiresAt {
			util.Success(ctx, util.R{
				P: util.P{
					Code:    200,
					Message: "token过期",
				},
			})
			ctx.Abort()
			return
		}
		ctx.Set("id", tokenStruck.ID)
		ctx.Set("username", tokenStruck.UserName)
		ctx.Next()
	}
}
